1 /** 2 * This module is a submodule of devisualization.image 3 * 4 * Provides optional interfaces which declares an class based image as well as reusable concepts such as a swappable image implementation dependent upon a provided image implementation. 5 * 6 * License: 7 * Copyright Devisualization (Richard Andrew Cattermole) 2014 - 2017. 8 * Distributed under the Boost Software License, Version 1.0. 9 * (See accompanying file LICENSE_1_0.txt or copy at 10 * http://www.boost.org/LICENSE_1_0.txt) 11 */ 12 module devisualization.image.interfaces; 13 import devisualization.image.primitives; 14 import std.experimental.color; 15 import stdx.allocator : IAllocator, ISharedAllocator, theAllocator, processAllocator; 16 import std.traits : isPointer, PointerTarget, isUnsigned; 17 18 /** 19 * Interface that defines the root methods required for a class/struct to be an image storage type.$(BR) 20 * Similar in purpose as InputRange is to ranges. 21 * 22 * Must have a constructor that takes in the width and height as arguments. 23 * ------------ 24 * this(size_t x, size_t y, IAllocator allocator = theAllocator()) 25 * ------------ 26 * Optionally may receive an allocator for allocating the pixel data from. The default is via the garbage collector. 27 * 28 * The width and height should not be 0. Instead for dummy test code use 1.$(BR) 29 * Fields cannot be used in place of methods. 30 */ 31 interface ImageStorage(Color) if (isColor!Color) { 32 @property { 33 /// Gets the width of the image 34 size_t width() @nogc nothrow @safe; 35 /// Ditto 36 size_t width() @nogc nothrow @safe shared; 37 38 /// Gets the height of the image 39 size_t height() @nogc nothrow @safe; 40 /// Ditto 41 size_t height() @nogc nothrow @safe shared; 42 } 43 44 /** 45 * Get a pixel given the position. 46 * 47 * Params: 48 * x = X position in image 49 * y = Y position in image 50 * 51 * Throws: 52 * If $(D x) or $(D y) coordinate is outside of the image boundries. 53 * 54 * Returns: 55 * The pixel color at point. 56 */ 57 Color getPixel(size_t x, size_t y) @nogc @safe; 58 /// Ditto 59 Color getPixel(size_t x, size_t y) @nogc @safe shared; 60 61 /** 62 * Sets a pixel given the position. 63 * 64 * Params: 65 * x = X position in image 66 * y = Y position in image 67 * value = The color to assign to the pixel 68 * 69 * Throws: 70 * If $(D x) or $(D y) coordinate is outside of the image boundries. 71 */ 72 void setPixel(size_t x, size_t y, Color value) @nogc @safe; 73 /// Ditto 74 void setPixel(size_t x, size_t y, Color value) @nogc @safe shared; 75 76 /** 77 * Get a pixel given the position. 78 * 79 * Params: 80 * x = X position in image 81 * y = Y position in image 82 * 83 * Throws: 84 * If $(D x) or $(D y) coordinate is outside of the image boundries. 85 * 86 * Returns: 87 * The pixel color at point. 88 * 89 * See_Also: 90 * getPixel 91 */ 92 Color opIndex(size_t x, size_t y) @nogc @safe; 93 /// Ditto 94 Color opIndex(size_t x, size_t y) @nogc @safe shared; 95 96 /** 97 * Sets a pixel given the position. 98 * 99 * Params: 100 * x = X position in image 101 * y = Y position in image 102 * value = The color to assign to the pixel 103 * 104 * Throws: 105 * If $(D x) or $(D y) coordinate is outside of the image boundries. 106 * 107 * See_Also: 108 * setPixel 109 */ 110 void opIndexAssign(Color value, size_t x, size_t y) @nogc @safe; 111 /// Ditto 112 void opIndexAssign(Color value, size_t x, size_t y) @nogc @safe shared; 113 114 /** 115 * Resizes the data store. 116 * 117 * Will not scale and $(U may) lose data in the process. 118 * 119 * Params: 120 * newWidth = The width the data store will become 121 * newHeight = The height the data store will become 122 * 123 * Returns: 124 * If the data store was successfully resized 125 */ 126 bool resize(size_t newWidth, size_t newHeight) @safe; 127 /// Ditto 128 bool resize(size_t newWidth, size_t newHeight) @safe shared; 129 } 130 131 /** 132 * Adds the ability to get a pixel based upon its offset. 133 * 134 * X and Y coordinate can be calculate using: 135 * ----------------- 136 * ImageStorage!Color image = ...; 137 * size_t offset = ...; 138 * size_t x = offset % image.height; 139 * size_t y = offset / image.height; 140 * ----------------- 141 * 142 * Offset from X and Y coordinate can be calculated as: 143 * ----------------- 144 * ImageStorage!Color image = ...; 145 * size_t x, y = ...; 146 * size_t offset = x + (y * image.width); 147 * ----------------- 148 * 149 * See_Also: 150 * ImageStorage 151 */ 152 interface ImageStorageOffset(Color) { 153 @property { 154 /// The number of pixels in total 155 size_t count() @nogc nothrow @safe; 156 /// Ditto 157 size_t count() @nogc nothrow @safe shared; 158 } 159 160 /** 161 * Get a pixel given the position. 162 * 163 * Params: 164 * offset = The offset of the pixel 165 * 166 * Throws: 167 * If $(D offset) coordinate is outside of the image boundary. 168 * 169 * Returns: 170 * The pixel color at point. 171 */ 172 Color getPixelAtOffset(size_t offset) @nogc @safe; 173 /// Ditto 174 Color getPixelAtOffset(size_t offset) @nogc @safe shared; 175 176 /** 177 * Set a pixel given the position. 178 * 179 * Params: 180 * offset = The offset of the pixel 181 * value = The color to assign to the pixel 182 * 183 * Throws: 184 * If $(D offset) coordinate is outside of the image boundary. 185 */ 186 void setPixelAtOffset(size_t offset, Color value) @nogc @safe; 187 /// Ditto 188 void setPixelAtOffset(size_t offset, Color value) @nogc @safe shared; 189 190 /** 191 * Get a pixel given the position. 192 * 193 * Params: 194 * offset = The offset of the pixel 195 * 196 * Throws: 197 * If $(D offset) coordinate is outside of the image boundary. 198 * 199 * Returns: 200 * The pixel color at point. 201 * 202 * See_Also: 203 * getPixelAtOffset 204 */ 205 Color opIndex(size_t offset) @nogc @safe; 206 /// Ditto 207 Color opIndex(size_t offset) @nogc @safe shared; 208 209 /** 210 * Set a pixel given the position. 211 * 212 * Params: 213 * offset = The offset of the pixel 214 * value = The color to assign to the pixel 215 * 216 * Throws: 217 * If $(D offset) coordinate is outside of the image boundary. 218 * 219 * See_Also: 220 * setPixelAtOffset 221 */ 222 void opIndexAssign(Color value, size_t offset) @nogc @safe; 223 /// Ditto 224 void opIndexAssign(Color value, size_t offset) @nogc @safe shared; 225 } 226 227 /** 228 * Wraps an image implementation up so that the exact implementation doesn't matter. 229 * As long as the color type is known it can be passed around freely. 230 * It does $(I not) allocate to perform its functions and can be safely used on the stack. 231 */ 232 struct SwappableImage(Color) if (isColor!Color) { 233 this() @disable; 234 this(size_t width, size_t height, IAllocator alloc=theAllocator()) @disable; 235 236 ~this() @safe { 237 if (destroyerDel !is null) { 238 destroyerDel(); 239 } 240 } 241 242 /** 243 * Constructs a swappable image using a specific image storage type as its value. 244 * 245 * If the image storage type does not support ImageStorageOffset it will add support.$(BR) 246 * If the color type specified is not the same type for the image storage instance then it'll auto convert. It is the responsiblity of the user to check for precision gain/lost.$(BR) 247 * Supports deallocating of the image provided, using the allocator, when the destructor is called. If the allocator is not provided it will not deallocate it. 248 * 249 * Params: 250 * fromImage = The instance to take delegates from 251 * allocator = Allocator to deallocate the image if provided when destructor is called 252 * 253 * See_Also: 254 * ImageStorage, ImageStorageOffset 255 */ 256 this(T)(T from, IAllocator allocator = null) @nogc @trusted if (((!isPointer!T && isImage!T) || (isPointer!T && isImage!(PointerTarget!T))) && 257 isUnsigned!(ImageIndexType!T) && (ImageIndexType!T).sizeof <= (void*).sizeof) 258 in { 259 static if (is(T == class)) 260 assert(from !is null); 261 } body { 262 static if (isPointer!T && isImage!(PointerTarget!T)) 263 alias ImageRealType = PointerTarget!T; 264 else 265 alias ImageRealType = T; 266 267 static if (!isPointer!T && is(ImageRealType == struct)) 268 origin_ = cast(void*)&from; 269 else 270 origin_ = cast(void*)from; 271 272 this.allocator = allocator; 273 if (allocator !is null) { 274 static if (!isPointer!T && is(T == struct)) 275 destroyerDel = &destroyerHandler!(ImageRealType*); 276 else 277 destroyerDel = &destroyerHandler!T; 278 } 279 280 widthDel = &from.width; 281 heightDel = &from.height; 282 resizeDel = cast(bool delegate(size_t, size_t) @safe)&from.resize; 283 284 pixelAt_ = cast(void delegate())&from.getPixel; 285 pixelAtDel = &pixelAtCompatFunc!(ImageColor!ImageRealType); 286 287 pixelStoreAt_ = cast(void delegate())&from.setPixel; 288 pixelStoreAtDel = &pixelStoreAtCompatFunc!(ImageColor!ImageRealType); 289 290 static if (supportsImageOffset!ImageRealType) { 291 countDel = &from.count; 292 293 pixelAtOffset_ = cast(void delegate())&from.getPixelAtOffset; 294 pixelAtOffsetDel = &pixelAtOffsetCompatFunc!(ImageColor!ImageRealType); 295 296 pixelStoreAtOffset_ = cast(void delegate())&from.setPixelAtOffset; 297 pixelStoreAtOffsetDel = &pixelStoreAtOffsetCompatFunc!(ImageColor!ImageRealType); 298 } else { 299 countDel = &countNotSupported; 300 pixelAtOffsetDel = &pixelAtOffsetNotSupported; 301 pixelStoreAtOffsetDel = &pixelStoreAtOffsetNotSupported; 302 } 303 } 304 305 @property { 306 // Gets the width of the image 307 size_t width() @nogc nothrow @safe { return widthDel(); } 308 309 /// Gets the height of the image 310 size_t height() @nogc nothrow @safe { return heightDel(); } 311 312 /// Gets the number of pixels the image contains 313 size_t count() @nogc nothrow @safe { return countDel(); } 314 315 /** 316 * A pointer to the original image storage type. 317 * Use at your own risk. 318 */ 319 void* original() @nogc nothrow @safe { return origin_; } 320 } 321 322 /** 323 * Get a pixel given the position. 324 * 325 * Params: 326 * x = X position in image 327 * y = Y position in image 328 * 329 * Throws: 330 * If $(D x) or $(D y) coordinate is outside of the image boundries. 331 * 332 * Returns: 333 * The pixel color at point. 334 */ 335 Color getPixel(size_t x, size_t y) @nogc @safe { return pixelAtDel(x, y); } 336 337 /** 338 * Sets a pixel given the position. 339 * 340 * Params: 341 * x = X position in image 342 * y = Y position in image 343 * value = The color to assign to the pixel 344 * 345 * Throws: 346 * If $(D x) or $(D y) coordinate is outside of the image boundries. 347 */ 348 void setPixel(size_t x, size_t y, Color value) @nogc @safe { pixelStoreAtDel(x, y, value); } 349 350 /** 351 * Get a pixel given the position. 352 * 353 * Params: 354 * offset = The offset of the pixel 355 * 356 * Throws: 357 * If $(D offset) coordinate is outside of the image boundary. 358 * 359 * Returns: 360 * The pixel color at point. 361 */ 362 Color getPixelAtOffset(size_t offset) @nogc @safe { return pixelAtOffsetDel(offset); }; 363 364 /** 365 * Set a pixel given the position. 366 * 367 * Params: 368 * offset = The offset of the pixel 369 * value = The color to assign to the pixel 370 * 371 * Throws: 372 * If $(D offset) coordinate is outside of the image boundary. 373 */ 374 void setPixelAtOffset(size_t offset, Color value) @nogc @safe { pixelStoreAtOffsetDel(offset, value); } 375 376 /** 377 * Get a pixel given the position. 378 * 379 * Params: 380 * x = X position in image 381 * y = Y position in image 382 * 383 * Throws: 384 * If $(D x) or $(D y) coordinate is outside of the image boundries. 385 * 386 * Returns: 387 * The pixel color at point. 388 * 389 * See_Also: 390 * getPixel 391 */ 392 Color opIndex(size_t x, size_t y) @nogc @safe { return getPixel(x, y); } 393 394 /** 395 * Sets a pixel given the position. 396 * 397 * Params: 398 * x = X position in image 399 * y = Y position in image 400 * value = The color to assign to the pixel 401 * 402 * Throws: 403 * If $(D x) or $(D y) coordinate is outside of the image boundries. 404 * 405 * See_Also: 406 * setPixel 407 */ 408 void opIndexAssign(Color value, size_t x, size_t y) @nogc @safe { setPixel(x, y, value); } 409 410 /** 411 * Get a pixel given the position. 412 * 413 * Params: 414 * offset = The offset of the pixel 415 * 416 * Throws: 417 * If $(D offset) coordinate is outside of the image boundary. 418 * 419 * Returns: 420 * The pixel color at point. 421 * 422 * See_Also: 423 * getPixelAtOffset 424 */ 425 Color opIndex(size_t offset) @nogc @safe { return getPixelAtOffset(offset); } 426 427 /** 428 * Set a pixel given the position. 429 * 430 * Params: 431 * offset = The offset of the pixel 432 * value = The color to assign to the pixel 433 * 434 * Throws: 435 * If $(D offset) coordinate is outside of the image boundary. 436 * 437 * See_Also: 438 * setPixelAtOffset 439 */ 440 void opIndexAssign(Color value, size_t offset) @nogc @safe { setPixelAtOffset(offset, value); } 441 442 /** 443 * Resizes the data store. 444 * 445 * Will not scale and $(U may) lose data in the process.$(BR) 446 * If the resize operation puts the image into a potentially errornous state, it should throw an exception. 447 * 448 * Params: 449 * newWidth = The width the data store will become 450 * newHeight = The height the data store will become 451 * 452 * Returns: 453 * If the data store was successfully resized 454 */ 455 bool resize(size_t newWidth, size_t newHeight) @safe { 456 return resizeDel(newWidth, newHeight); 457 } 458 459 private { 460 size_t delegate() @nogc @safe nothrow widthDel; 461 size_t delegate() @nogc @safe nothrow heightDel; 462 size_t delegate() @nogc @safe nothrow countDel; 463 464 Color delegate(size_t x, size_t y) @nogc @safe pixelAtDel; 465 void delegate(size_t x, size_t y, Color value) @nogc @safe pixelStoreAtDel; 466 Color delegate(size_t offset) @nogc @safe pixelAtOffsetDel; 467 void delegate(size_t offset, Color value) @nogc @safe pixelStoreAtOffsetDel; 468 469 bool delegate(size_t, size_t) @safe resizeDel; 470 471 void* origin_; 472 473 IAllocator allocator; 474 void delegate() @trusted destroyerDel; 475 476 void delegate() pixelAt_; 477 void delegate() pixelStoreAt_; 478 void delegate() pixelAtOffset_; 479 void delegate() pixelStoreAtOffset_; 480 481 void destroyerHandler(T)() @trusted { 482 import stdx.allocator : dispose; 483 allocator.dispose(cast(T)origin_); 484 } 485 486 Color pixelAtCompatFunc(FROM)(size_t x, size_t y) @nogc @trusted { 487 auto del = cast(FROM delegate(size_t x, size_t y) @nogc @safe) pixelAt_; 488 auto got = del(x, y); 489 490 static if (is(FROM == Color)) { 491 return got; 492 } else { 493 return convertColor!Color(got); 494 } 495 } 496 497 void pixelStoreAtCompatFunc(FROM)(size_t x, size_t y, Color value) @nogc @trusted { 498 auto del = cast(void delegate(size_t x, size_t y, FROM value) @nogc @safe) pixelStoreAt_; 499 500 static if (is(FROM == Color)) { 501 del(x, y, value); 502 } else { 503 del(x, y, convertColor!FROM(value)); 504 } 505 } 506 507 Color pixelAtOffsetCompatFunc(FROM)(size_t offset) @nogc @trusted { 508 auto del = cast(FROM delegate(size_t offset) @nogc @safe) pixelAtOffset_; 509 510 static if (is(FROM == Color)) { 511 return del(offset); 512 } else { 513 return convertColor!Color(del(offset)); 514 } 515 } 516 517 void pixelStoreAtOffsetCompatFunc(FROM)(size_t offset, Color value) @nogc @trusted { 518 auto del = cast(void delegate(size_t offset, FROM value) @nogc @safe) pixelStoreAtOffset_; 519 520 static if (is(FROM == Color)) { 521 del(offset, value); 522 } else { 523 del(offset, convertColor!FROM(value)); 524 } 525 } 526 527 size_t countNotSupported() @nogc nothrow @safe { 528 return width() * height(); 529 } 530 531 Color pixelAtOffsetNotSupported(size_t offset) @nogc @trusted { 532 return getPixel(offset % height(), offset / (height() + 1)); 533 } 534 535 void pixelStoreAtOffsetNotSupported(size_t offset, Color value) @nogc @trusted { 536 setPixel(offset % height(), offset / (height() + 1), value); 537 } 538 } 539 } 540 541 version(unittest) package { 542 class MyTestImage(Color) : ImageStorage!Color { 543 private { 544 const size_t width_, height_; 545 IAllocator allocator; 546 Color[][] data; 547 } 548 549 this(size_t width, size_t height, IAllocator allocator = theAllocator()) { 550 import stdx.allocator : makeArray; 551 this.allocator = allocator; 552 553 width_ = width; 554 height_ = height; 555 556 data = allocator.makeArray!(Color[])(width); 557 558 foreach(_; 0 .. width) { 559 data[_] = allocator.makeArray!Color(height); 560 } 561 } 562 563 @property { 564 size_t width() @nogc nothrow @safe{ return width_; } 565 size_t width() @nogc nothrow @safe shared{ return width_; } 566 size_t height() @nogc nothrow @safe { return height_; } 567 size_t height() @nogc nothrow @safe shared { return height_; } 568 } 569 570 Color getPixel(size_t x, size_t y) @nogc @safe { return data[x][y]; } 571 Color getPixel(size_t x, size_t y) @nogc @safe shared { return data[x][y]; } 572 void setPixel(size_t x, size_t y, Color value) @nogc @safe { data[x][y] = value; } 573 void setPixel(size_t x, size_t y, Color value) @nogc shared @safe { data[x][y] = value; } 574 Color opIndex(size_t x, size_t y) @nogc @safe { return getPixel(x, y); } 575 Color opIndex(size_t x, size_t y) @nogc @safe shared { return getPixel(x, y); } 576 void opIndexAssign(Color value, size_t x, size_t y) @nogc @safe { setPixel(x, y, value); } 577 void opIndexAssign(Color value, size_t x, size_t y) @nogc @safe shared { setPixel(x, y, value); } 578 579 bool resize(size_t, size_t) @nogc @safe { return false; } 580 bool resize(size_t, size_t) @nogc @safe shared { return false; } 581 } 582 } 583 584 unittest { 585 import stdx.allocator : make, theAllocator; 586 587 SwappableImage!RGB8 image = SwappableImage!(RGB8)(theAllocator.make!(MyTestImage!RGB8)(8, 3)); 588 RGB8 value = image[0, 0]; 589 } 590 591 unittest { 592 import stdx.allocator : make, theAllocator; 593 594 SwappableImage!RGBA8 image = SwappableImage!(RGBA8)(theAllocator.make!(MyTestImage!RGB8)(8, 3)); 595 RGBA8 value = image[0, 0]; 596 } 597 598 unittest { 599 import stdx.allocator : make, theAllocator; 600 601 SwappableImage!RGB8 image = SwappableImage!(RGB8)(theAllocator.make!(MyTestImage!RGB8)(8, 3)); 602 size_t count = image.count(); 603 assert(count == 24); 604 } 605 606 /** 607 * Constructs an input range over an image 608 * For every pixel get x + y and the color value 609 * 610 * TODO: Scan line version of this? 611 * 612 * Params: 613 * from = The image to create a range upon 614 * 615 * Returns: 616 * An input range to get every pixel along with its X and Y coordinates. 617 */ 618 auto rangeOf(Color)(SwappableImage!Color* from) @nogc nothrow @safe { 619 return RangeOf!Color(from, 0, 0); 620 } 621 622 /** 623 * Constructs an input range over an image 624 * For every pixel get x + y and the color value 625 * 626 * Will $(B allocate) a new SwappableImage to wrap around the image given on the heap. 627 * The returned input range will auto deallocate the SwappableImage that was allocated when it becomes empty. 628 * 629 * TODO: Scan line version of this? 630 * 631 * Params: 632 * from = The image to create a range upon 633 * allocator = The allocator to allocate the SwappableImage instance on the heap. Will auto free it when destructed. 634 * 635 * Returns: 636 * An input range to get every pixel along with its X and Y coordinates. 637 */ 638 auto rangeOf(Image)(Image from, IAllocator allocator = theAllocator()) @trusted if (isImage!Image) { 639 import stdx.allocator : make; 640 alias Color = ImageColor!Image; 641 642 SwappableImage!Color* inst = allocator.make!(SwappableImage!Color)(from); 643 return RangeOf!Color(inst, 0, 0, allocator); 644 } 645 646 private { 647 struct RangeOf(Color) { 648 private { 649 SwappableImage!Color* input; 650 size_t offsetX; 651 size_t offsetY; 652 IAllocator allocator; 653 } 654 655 private this(SwappableImage!Color* input, size_t offsetX, size_t offsetY, IAllocator allocator = null) @nogc nothrow @trusted { 656 this.input = input; 657 this.offsetX = offsetX; 658 this.offsetY = offsetY; 659 this.allocator = allocator; 660 } 661 662 @property { 663 auto front() @nogc @safe { 664 return PixelPoint!Color(input.getPixel(offsetX, offsetY), offsetX, offsetY, input.width, input.height); 665 } 666 667 bool empty() @trusted { 668 bool ret = offsetX == 0 && offsetY == input.height(); 669 670 if (ret) { 671 import stdx.allocator : dispose; 672 // deallocates the input, if the allocator is provided 673 674 if (this.allocator !is null && input !is null) { 675 allocator.dispose(input); 676 input = null; 677 } 678 } 679 680 return ret; 681 } 682 } 683 684 void popFront() @nogc nothrow @safe { 685 if (offsetX == input.width() - 1) { 686 offsetY++; 687 offsetX = 0; 688 } else { 689 offsetX++; 690 } 691 } 692 } 693 } 694 695 unittest { 696 size_t count; 697 698 foreach(pixel; new MyTestImage!RGB8(2, 2).rangeOf) { 699 count++; 700 } 701 702 assert(count == 4); 703 704 auto aRange = new MyTestImage!RGB8(2, 2).rangeOf; 705 auto pixel = aRange.front; 706 707 assert(pixel.x == 0); 708 assert(pixel.y == 0); 709 710 aRange.popFront; 711 } 712 713 /** 714 * A single pixel inside an image. 715 * 716 * To be returned from input ranges. 717 */ 718 struct PixelPoint(Color) if (isColor!Color) { 719 /// 720 Color value; 721 alias value this; 722 723 /// 724 size_t x, y; 725 726 /// 727 size_t imageWidth, imageHeight; 728 } 729 730 /// Wraps a struct image storage type into a class 731 final class ImageObject(Impl) : ImageStorage!(ImageColor!Impl) if (is(Impl == struct) && isUnsigned!(ImageIndexType!Impl) && (ImageIndexType!Impl).sizeof <= (void*).sizeof) { 732 import std.traits : Unqual; 733 alias Color = ImageColor!Impl; 734 735 // If I could make this private, I would... 736 this()(Impl* instance) @trusted { 737 swpInst = instance; 738 } 739 740 static if (is(Impl == shared) && __traits(compiles, {new shared Impl(0, 0, cast(shared(ISharedAllocator))null);})) { 741 // Ditto 742 this(size_t width, size_t height, shared(ISharedAllocator) allocator = processAllocator()) @trusted shared { 743 import stdx.allocator : make; 744 swpInst = cast(shared)allocator.make!(Unqual!Impl)(width, height, allocator); 745 _salloc = allocator; 746 } 747 } else static if (!is(Impl == shared) && __traits(compiles, {new Impl(0, 0, cast(IAllocator)null);})) { 748 // Ditto 749 this(size_t width, size_t height, IAllocator allocator = theAllocator()) @trusted { 750 import stdx.allocator : make; 751 swpInst = allocator.make!Impl(width, height, allocator); 752 _alloc = allocator; 753 } 754 } 755 756 @property { 757 size_t width() @nogc nothrow @safe {return swpInst.width;} 758 size_t width() @nogc nothrow @trusted shared {return (cast(Unqual!Impl*)swpInst).width;} 759 size_t height() @nogc nothrow @safe {return swpInst.height;} 760 size_t height() @nogc nothrow @trusted shared {return (cast(Unqual!Impl*)swpInst).height;} 761 } 762 763 Color getPixel(size_t x, size_t y) @nogc @safe {return swpInst.getPixel(x, y);} 764 Color getPixel(size_t x, size_t y) @nogc @trusted shared {return (cast(Unqual!Impl*)swpInst).getPixel(x, y);} 765 void setPixel(size_t x, size_t y, Color value) @nogc @safe {swpInst.setPixel(x, y, value);} 766 void setPixel(size_t x, size_t y, Color value) @nogc @trusted shared {(cast(Unqual!Impl*)swpInst).setPixel(x, y, value);} 767 Color opIndex(size_t x, size_t y) @nogc @safe {return swpInst.opIndex(x, y);} 768 Color opIndex(size_t x, size_t y) @nogc @trusted shared {return (cast(Unqual!Impl*)swpInst).opIndex(x, y);} 769 void opIndexAssign(Color value, size_t x, size_t y) @nogc @safe {swpInst.opIndexAssign(value, x, y);} 770 void opIndexAssign(Color value, size_t x, size_t y) @nogc @trusted shared {(cast(Unqual!Impl*)swpInst).opIndexAssign(value, x, y);} 771 bool resize(size_t newWidth, size_t newHeight) @safe {return swpInst.resize(newWidth, newHeight);} 772 bool resize(size_t newWidth, size_t newHeight) @trusted shared {return (cast(Unqual!Impl*)swpInst).resize(newWidth, newHeight);} 773 774 private { 775 import std.traits : Unqual; 776 777 IAllocator _alloc; 778 shared(ISharedAllocator) _salloc; 779 Impl* swpInst; 780 781 ~this() { 782 import stdx.allocator : dispose; 783 static if (is(Impl == shared)) { 784 if (_salloc !is null) 785 _salloc.dispose(cast(Unqual!Impl*)swpInst); 786 } else { 787 if (_alloc !is null) 788 _alloc.dispose(swpInst); 789 } 790 } 791 } 792 } 793 794 /** 795 * Constructs an object around an image storage type. 796 * 797 * Params: 798 * width = The width to assign 799 * height = The height to assign 800 * allocator = Allocator to use 801 * 802 * Returns: 803 * An ImageObject wrapper around the implementation specified. 804 * 805 * See_Also: 806 * ImageObject 807 */ 808 auto imageObject(Impl)(size_t width, size_t height, IAllocator allocator = theAllocator) @trusted 809 if (is(Impl == struct) && isUnsigned!(ImageIndexType!Impl) && (ImageIndexType!Impl).sizeof <= (void*).sizeof && !is(Impl==shared)) { 810 import stdx.allocator : make; 811 return allocator.make!(ImageObject!Impl)(width, height, allocator); 812 } 813 814 /// Ditto 815 auto imageObject(Impl)(size_t width, size_t height, shared(ISharedAllocator) allocator = processAllocator) @trusted 816 if (is(Impl == struct) && isUnsigned!(ImageIndexType!Impl) && (ImageIndexType!Impl).sizeof <= (void*).sizeof && is(Impl==shared)) { 817 import stdx.allocator : make; 818 return allocator.make!(shared(ImageObject!Impl))(width, height, allocator); 819 } 820 821 /** 822 * Constructs an object around an image storage type. 823 * 824 * Params: 825 * instance = Instance to wrap 826 * allocator = Allocator to use 827 * 828 * Returns: 829 * An ImageObject wrapper around the implementation specified. 830 * 831 * See_Also: 832 * ImageObject 833 */ 834 auto imageObject(Impl)(Impl* instance, IAllocator allocator = theAllocator) @trusted 835 if (is(Impl == struct) && isUnsigned!(ImageIndexType!Impl) && (ImageIndexType!Impl).sizeof <= (void*).sizeof && !is(Impl==shared)) { 836 import stdx.allocator : make; 837 return allocator.make!(ImageObject!Impl)(instance); 838 } 839 840 /// Ditto 841 auto imageObject(Impl)(Impl* instance, shared(ISharedAllocator) allocator = processAllocator) @trusted 842 if (is(Impl == struct) && isUnsigned!(ImageIndexType!Impl) && (ImageIndexType!Impl).sizeof <= (void*).sizeof && is(Impl==shared)) { 843 import stdx.allocator : make; 844 return allocator.make!(shared(ImageObject!Impl))(instance); 845 } 846 847 /** 848 * Constructs an object based upon an existing image. 849 * 850 * Params: 851 * from = Instance to wrap 852 * allocator = Allocator to use 853 * 854 * Returns: 855 * An ImageObject wrapper around the implementation specified. 856 * 857 * See_Also: 858 * ImageObject 859 */ 860 auto imageObjectFrom(Impl, Image)(Image from, IAllocator allocator = theAllocator) @trusted 861 if (is(Impl == struct) && isImage!Impl && isImage!Image && isUnsigned!(ImageIndexType!Impl) && (ImageIndexType!Impl).sizeof <= (void*).sizeof && !is(Impl==shared)) { 862 import devisualization.image.primitives : copyTo; 863 import stdx.allocator : make; 864 865 return from.copyTo(imageObject!Impl(from.width, from.height, allocator)); 866 } 867 868 /// Ditto 869 auto imageObjectFrom(Impl, Image)(Image from, shared(ISharedAllocator) allocator = processAllocator) @trusted 870 if (is(Impl == struct) && isImage!Impl && isImage!Image && isUnsigned!(ImageIndexType!Impl) && (ImageIndexType!Impl).sizeof <= (void*).sizeof && is(Impl==shared)) { 871 import devisualization.image.primitives : copyTo; 872 import stdx.allocator : make; 873 874 return from.copyTo(imageObject!Impl(from.width, from.height, allocator)); 875 }